using System;
using System.Xml.Serialization;
using SharpMud.Server.Systems.Scripting.Processing;

namespace SharpMud.MudLib.SAMPLE.Commands
{
	public class CommandMetadatum
	{
		private SharpMud.Text.Matching.StringMatcher[]	_CommandAliases;
		private System.Guid[]							_CommandGuids;
		
		private ICompiledScriptHandle _CompiledScriptHandle;

		protected CommandMetadatum()
		{
		}

		protected CommandMetadatum(SharpMud.Server.Systems.Scripting.Processing.ICompiledScriptHandle compiledScriptHandle)
		{
			if(!IsValidCommandType(commandType))
			{
				throw new ArgumentException("A command must interface with 'ICommand' or a derived interface.","commandType");
			}

			System.Collections.ArrayList matcher_al = new System.Collections.ArrayList();
			foreach(Metadata.AliasAttribute aa in compiledScriptHandle.GetCustomAttributes(typeof(Metadata.AliasAttribute),false))
			{
				matcher_al.AddRange(aa.CommandMatchers);
			}
			_CommandAliases = (SharpMud.Text.Matching.StringMatcher[])matcher_al.ToArray(typeof(SharpMud.Text.Matching.StringMatcher));

			System.Collections.ArrayList guid_al = new System.Collections.ArrayList();
			foreach(Metadata.GuidAttribute ga in compiledScriptHandle.GetCustomAttributes(typeof(Metadata.GuidAttribute),false))
			{
				guid_al.Add(ga.CommandGuid);
			}
			_CommandGuids = (System.Guid[])guid_al.ToArray(typeof(System.Guid));
		}

		/// <summary>
		/// The unique Guid of this command.
		/// </summary>
		public System.Guid Guid
		{
			get
			{
				if(_CommandGuids.Length==0)
					return System.Guid.Empty;
				else
					return _CommandGuids[0];
			}
		}

		/// <summary>
		/// The full type name of the command
		/// </summary>
		public string CommandTypeName
		{
			get
			{
				return this._CommandType.FullName;
			}
		}

		public SharpMud.Text.Matching.StringMatcher[] CommandAliases
		{
			get
			{
				return this._CommandAliases;
			}
		}

        public ICompiledScriptHandle CompiledScriptHandle
        {
            get
            {
                return _CompiledScriptHandle;
            }
        }

		/// <summary>
		/// Determines if the given character can verify the existence of the command (other than trying
		/// to execute it)
		/// </summary>
		public bool IsVisibleTo(Entities.ICharacter character)
		{
            foreach (Metadata.VisibilityRequirementAttribute vra in _CompiledScriptHandle.GetCustomAttributes(typeof(Metadata.VisibilityRequirementAttribute), true))
			{
				if(vra.AllowsVisibilityTo(character)==false)
					return false;
			}
			return true;
		}

		/// <summary>
		/// Determines if the given character can execute the command
		/// </summary>
		public bool IsExecutableBy(Entities.ICharacter character)
		{
			foreach(Metadata.ExecutionRequirementAttribute era in _CompiledScriptHandle.GetCustomAttributes(typeof(Metadata.ExecutionRequirementAttribute),true))
			{
				if(era.AllowsExecutionFor(character)==false)
					return false;
			}
			return true;
		}

		/// <summary>
		/// Returns the message that should be given to a character attempting to execute command.
		/// </summary>
		/// <remarks>The message should not contain any formating information, except for punctuation.</remarks>
		public string ExecutionFailureExplanation(Entities.ICharacter character)
		{
            foreach (Metadata.ExecutionRequirementAttribute era in _CompiledScriptHandle.GetCustomAttributes(typeof(Metadata.ExecutionRequirementAttribute), true))
			{
				if(era.AllowsExecutionFor(character)==false)
					return era.ExecutionFailureExplanation;
			}
			return null;
		}

		#region Static Stuff
		public static CommandMetadatum FromLoadedType(System.Type type)
		{
			if(!IsValidCommandType(type))
				return null;
			else
			{
				return new CommandMetadatum(type);
			}
		}
		public static CommandMetadatum FromTypeLocation(SharpMud.DynamicTyping.AssembliedTypeLocation typeLocation)
		{
			return FromLoadedType(typeLocation.AcquireType());
		}
		public static bool IsValidCommandType(System.Type type)
		{
			foreach(System.Type type_interface in type.GetInterfaces())
			{
				if(type_interface==typeof(ICommand)
					||
					type_interface.IsSubclassOf(typeof(ICommand)))
				{
					return true;
				}
			}
			return false;
		}
		#endregion
	}
}